#include <Kokkos_Core.hpp>
#include <iostream>

int main(int argc, char* argv[]) {
  Kokkos::initialize(argc, argv);
  {
    std::cout << "##########################\n";
    std::cout << "KOKKOS CONFIG             \n";
    std::cout << "##########################\n";
    std::ostringstream msg;
    std::cout << "Kokkos configuration" << std::endl;
    Kokkos::print_configuration(msg);
    std::cout << msg.str();
    std::cout << "##########################\n";

    typedef Kokkos::View<double*> ViewVectorType;
    typedef Kokkos::View<double**> ViewMatrixType;

    int N = 10, M = 10;

    // Allocate x vectors on device.
    ViewVectorType x("x", M);
    ViewVectorType y("y", N);
    ViewMatrixType A("A", N, M);

    // Create host mirrors of device views.
    ViewVectorType::HostMirror h_x = Kokkos::create_mirror_view(x);
    ViewVectorType::HostMirror h_y = Kokkos::create_mirror_view(y);
    ViewMatrixType::HostMirror h_A = Kokkos::create_mirror_view(A);

    // Initialize x vector on host.
    for (int i = 0; i < M; ++i) {
      h_x(i) = 1.0;
    }

    // Initialize y vector on host.
    for (int i = 0; i < N; ++i) {
      h_y(i) = 1.0;
    }

    // Initialize A matrix on host.
    for (int j = 0; j < N; ++j) {
      for (int i = 0; i < M; ++i) {
        h_A(j, i) = 1.0;
      }
    }

    // Deep copy host views to device views.
    Kokkos::deep_copy(y, h_y);
    Kokkos::deep_copy(x, h_x);
    Kokkos::deep_copy(A, h_A);

    double result = 0;
    Kokkos::parallel_reduce( "yAx", N, [=] (int j, double& update) {
          double tmp= 0;

          for (int i = 0; i < M; ++i) {
            tmp += A(j, i) * x(i);
          }

          update += y(j) * tmp;
        },
        result);

  std::cout << result << std::endl;
  }

  Kokkos::finalize();
}